/** 定义裁剪模式 */
const CropMode = {
    none: 0, // 不显示裁剪框
    rect: 1, //矩形裁剪框
    polygon: 2, //多边形裁剪框
    spec: 3, //指定边长比例裁剪框,如1寸证件照
    sourceScale: 4, //原图片比例
    heart: 5 ///心形裁剪框
}
// 定义point
class Point {
    x = 0;
    y = 0;
    constructor(x = 0, y = 0) {
        this.x = x;
        this.y = y;
    }
}
// 尺寸类
class Size {
    left = 0;
    top = 0;
    right = 0;
    bottom = 0;

    // 通过对象初始化
    constructor(size) {
        if (size === undefined || Object.keys(size).length === 0) {
            this.left = 0;
            this.top = 0;
            this.right = 0;
            this.bottom = 0;
        } else {
            this.set(size);
        }
    }

    set(size) {
        Object.keys(size).forEach((value) => {
            if (value === 'left') {
                this.left = size[value];
            } else if (value === 'top') {
                this.top = size[value];
            } else if (value === 'right') {
                this.right = size[value];
            } else if (value === 'bottom') {
                this.bottom = size[value];
            }
            // else if (value === 'width') {
            //     this.width = size[value];
            // } else if (value === 'height') {
            //     this.height = size[value];
            // }
        })
    }
    // 输出
    getWidth() {
        return (this.right - this.left);
    }
    getHeight() {
        return (this.bottom - this.top);
    }
    getCenter() {
        return new Point((this.left + this.right) / 2, (this.top + this.bottom) / 2);
    }
}
/**定义操作对象,相当于保存每次操作后所有操作参数的快照 */
class Option {
    angle = 0; // 旋转角度
    cropbox = {
        points: [],
        type: CropMode.none,
        param: 4,
        center: new Point(0, 0),
        rect: new Size()
    }; // 裁剪框
    scale = {
        ratio: 1,
        center: new Point(0, 0),
        baseRatio: 1, // 容器显示完整图片的缩放比例
        rect: new Size()
    };
    overturn = {
        horizontal: 0,
        vertical: 0
    };
    // 缩放原点
    centerPoint = new Point(0, 0);
}
/** 定义操作栈用于恢复与重做 */
class OptionStatic {
    // 索引位置,标识当前位置
    index = -1;
    optionArr = new Array();

    constructor(opt = null) {
        let option = opt || new Option();
        // 初始化
        this.add(option);
    };

    // 压入操作
    add(params) {
        // 取出当前参数
        let opt = this.current();
        let vparms = JSON.parse(JSON.stringify(params));
        if (opt) {
            // 按照参数中的key值修改参数
            Object.keys(vparms).forEach(key => {
                if (opt.hasOwnProperty(key)) {
                    opt[key] = vparms[key];
                }
            });
        } else {
            // 无当前值的情况,为初始化,所以直接赋值
            opt = vparms;
        }
        this.optionArr.push(opt);
        this.index += 1;
    }
    // 重置,只保留第一个元素
    reset() {
        this.optionArr.splice(1, this.optionArr.length - 1);
        this.index = 0;
        return this.optionArr[this.index];
    }
    //前一步
    pre() {
        if (this.index > 0) {
            this.index -= 1;
        }
        return this.optionArr[this.index];
    }
    // 重做
    next() {
        if (this.index < this.optionArr.length - 1) {
            this.index += 1;
        }
        return this.optionArr[this.index];
    }
    // 获取当前元素
    current() {
        if (this.index !== -1) {
            //这里深拷贝
            let curr = JSON.parse(JSON.stringify(this.optionArr[this.index]));
            return curr;
        }
    }
}
// 拖动角点尺寸
const cornerWidth = 20;

// 拖拽时重新计算裁剪框位置的时间间隔
const interval = 30;

// 自定义裁剪框模块
Component({
    // 设置使用全局样式
    options: {
        addGlobalClass: true
    },
    // 属性列表
    properties: {
        // 图片源地址
        imgSource: String,
        // 显示旋转条,
        showSlider: {
            type:Boolean,
            value:true
        },
    },
    data: {
        // 当前状态参数,在对图片缩放\移动\旋转,以及裁剪时使用的状态参数
        currentOpt: new Option(),
        // 定义4个拖拽点位置
        leftTop: new Point(),
        rightTop: new Point(),
        rightBottom: new Point(),
        leftBottom: new Point(),

        // 源图片信息
        sourceImgInfo: new Size(),
        // 组件尺寸
        componentInfo: new Size(),
        // css裁剪路径
        cssClipPath: [],

        // 缩放中心点
        // centerPoint: new Point(0, 0),
        // 通过透明度隐藏或显示裁剪框
        opacity: "rgba(0, 0, 0,0.8)",
        // 操作栈,用于撤销\重做
        options: {},
        // 裁剪框调整大小时,图片动态缩放
        duration: false,
        // 辅助延迟
        delay: false,
        // 是否显示辅助网格
        disPlayAssisted: false,
        // 网格行列数
        grids: 9,
        // 保存拖动时间,间隔一定时间才触发重新计算裁剪框位置
        preTime: 0,
        // // 缩放参数
        // scale: {
        //     ratio: 1,
        // },
        // 移动图片时,记录起始坐标
        startMovePoint: null,
        // 操作栈,用于撤销\重做
        options: {},
        // 控制操作序列状态
        optionsState: {
            pre: undefined,
            next: undefined
        },

        // 临时数据
        // 选中的拖拽顶点
        tmpDragPoint: '',
        // 拖动点移动后位置
        tmpDragPosition: {}


    },
    methods: {
        // 转换坐标位置
        changePoint(sx, sy, pt) {
            let x = sx; //-this.data.comBasicPosition.left;
            let y = sy;//-this.data.comBasicPosition.top;
            // if(pt=='rt'){
            //     x = sx+cornerWidth;
            // }else if(pt=='rb'){
            //     x = sx+cornerWidth;
            //     y= sy +cornerWidth;
            // }else if(pt=='lb'){
            //     y = sy +cornerWidth;
            // }
            return [x, y];
        },
        /**设置裁剪框背景及辅助网格 */
        /*@param {*} flag true:开启,fale:关闭
         */
        setAssisted(flag, grids = 9) {
            if (flag) {
                // 开启
                this.setData({
                    delay: false,
                    opacity: "rgba(0,0,0,0.7)",
                    disPlayAssisted: true,
                    grids: grids
                })
            } else {
                // 关闭
                this.setData({
                    delay: true,
                    opacity: "rgba(0,0,0,0.9)",
                    disPlayAssisted: false,
                    grids: grids
                })
            }
        },
        // 调整裁剪框
        dragStart(e) {
            this.setData({
                tmpDragPoint: e.target.dataset.position,
            });
            this.setAssisted(true);
        },
        // 意外终止
        dragCancle(e) {
            this.setAssisted(false);
        },
        dragMove(e) {
            // 获取当前时间
            let myDate = new Date();
            let now = myDate.getTime();
            // 当时间间隔大于设定的绘制间隔时间时执行
            if (now - this.data.preTime > interval) {
                this.adjustOtherDragPoint(e);
                // 通过4个角点位置，重新调用生成裁剪框
                let cropPoints = this.generateCropbox();
                // 保存中间参数信息
                this.setData({
                    cssClipPath: cropPoints.clipPath,
                    preTime: now,
                });
            }
        },
        // 拖动角点时重新调整其他所有角点位置
        adjustOtherDragPoint(e) {
            // 只执行选中拖动点移动或位置变化的操作
            if (this.data.tmpDragPoint != e.target.dataset.position) return;
            // 拖动点移动后的位置
            const point = e.detail;
            // 定义4个顶点实时位置
            switch (this.data.tmpDragPoint) {
                case 'lt':
                    this.setData({
                        "rightTop.y": point.y,
                        "leftBottom.x": point.x,
                        "currentOpt.cropbox.rect.left": point.x,
                        "currentOpt.cropbox.rect.top": point.y,
                    });

                    break;
                case 'rt':
                    this.setData({
                        "leftTop.y": point.y,
                        "rightBottom.x": point.x,
                        "currentOpt.cropbox.rect.right": point.x + cornerWidth,
                        "currentOpt.cropbox.rect.top": point.y,
                    });

                    break;
                case 'rb':
                    this.setData({
                        "rightTop.x": point.x,
                        "leftBottom.y": point.y,
                        "currentOpt.cropbox.rect.right": point.x + cornerWidth,
                        "currentOpt.cropbox.rect.bottom": point.y + cornerWidth,
                    });

                    break;
                case 'lb':
                    this.setData({
                        "leftTop.x": point.x,
                        "rightBottom.y": point.y,
                        "currentOpt.cropbox.rect.left": point.x,
                        "currentOpt.cropbox.rect.bottom": point.y + cornerWidth,
                    });

                    break;
            }
            this.setData({
                "tmpDragPosition.x": point.x,
                "tmpDragPosition.y": point.y,
            })
        },
        // 最终保存拖动点位置
        adjustCurrentDragPoint() {
            //设置拖动点位置
            switch (this.data.tmpDragPoint) {
                case 'lt':
                    this.setData({
                        "leftTop.x": this.data.tmpDragPosition.x,
                        "leftTop.y": this.data.tmpDragPosition.y,
                        tmpDragPoint: ''
                    });
                    break;
                case 'rt':
                    this.setData({
                        "rightTop.x": this.data.tmpDragPosition.x,
                        "rightTop.y": this.data.tmpDragPosition.y,
                        tmpDragPoint: ''
                    });
                    break;
                case 'rb':
                    this.setData({
                        "rightBottom.x": this.data.tmpDragPosition.x,
                        "rightBottom.y": this.data.tmpDragPosition.y,
                        tmpDragPoint: ''
                    });
                    break;
                case 'lb':
                    this.setData({
                        "leftBottom.x": this.data.tmpDragPosition.x,
                        "leftBottom.y": this.data.tmpDragPosition.y,
                        tmpDragPoint: ''
                    });
                    break;
            }
        },
        // 结束拖动
        dragEnd(e) {
            this.adjustCurrentDragPoint();
            const ratio = this.getCropboxWithComponetRatio();

            const cropboxCenter = this.data.currentOpt.cropbox.rect.getCenter();
            const componentCenter = this.data.componentInfo.getCenter();
            let disX = (cropboxCenter.x - componentCenter.x) / this.data.currentOpt.scale.ratio;
            let disY = (cropboxCenter.y - componentCenter.y) / this.data.currentOpt.scale.ratio;
            const centerAndOrgin = this.getNewCenterAndOrgin(disX, disY);
            // 裁剪框位置
            const w = this.data.currentOpt.cropbox.rect.getWidth() * ratio;
            const h = this.data.currentOpt.cropbox.rect.getHeight() * ratio;

            // 将裁剪框按比例放大至最大时，各点位置
            const left = componentCenter.x - w / 2;
            const right = componentCenter.x + w / 2;
            const top = componentCenter.y - h / 2;
            const bottom = componentCenter.y + h / 2;
            // 记录图片整体被缩放的比例
            const currentRatio = this.data.currentOpt.scale.ratio * ratio;
            this.setData({
                // duration: true,
                "currentOpt.scale.center": centerAndOrgin.center,
                "currentOpt.scale.ratio": currentRatio,
                "currentOpt.centerPoint": centerAndOrgin.orgin,
                "currentOpt.cropbox.rect": new Size({ left: left, top: top, right: right, bottom: bottom }),
            })
            // 重新生成裁css剪框
            let cropPoints = this.generateCropbox();
            this.setData({
                cssClipPath: cropPoints.clipPath,
            });
            // 重新设置拖拽框位置
            this.setDragBoxPosition({ left: left, top: top, right: right, bottom: bottom });
            this.getSourceImgBoardInfo();

            this.setAssisted(false);
            this.setOptionsState(1);
        },
        // 移动图片=========================
        startMoveImg(e) {
            const pos = e.target.dataset.position || ''
            if (pos == '') {
                this.setAssisted(true);
                let [x, y] = this.changePoint(e.touches[0].pageX, e.touches[0].pageY);
                if (e.touches.length > 1) {
                    // 缩放图片
                    let [x1, y1] = this.changePoint(e.touches[1].pageX, e.touches[1].pageY);
                    this.setData({
                        // 记录初次触摸点
                        startMovePoint: [new Point(x, y), new Point(x1, y1)],
                        startRatio: this.data.currentOpt.scale.ratio,
                    });
                } else if (e.touches.length == 1) {
                    this.setData({
                        // 记录初次触摸点
                        startMovePoint: new Point(x, y)
                    });

                }
            }
        },
        bindMoveImg(e) {
            const pos = e.target.dataset.position || ''
            if (pos == '') {
                // 获取当前时间
                let myDate = new Date();
                let now = myDate.getTime();
                // 大于设定的时间间隔,移动图片
                if ((now - this.data.preTime) > interval) {
                    if (e.touches.length == 1) {
                        let [x, y] = this.changePoint(e.touches[0].pageX, e.touches[0].pageY);
                        this.moveImg(x, y);
                    } else if (e.touches.length > 1) {
                        this.scaleImage(e.touches)
                    }
                    // 重新记录本次事件触发时间
                    this.setData({
                        preTime: now
                    })
                }
            }
        },
        endMoveImg(e) {
            const pos = e.target.dataset.position || ''
            if (pos == '') {
                this.setData({
                    startMovePoint: null,
                });
                this.setAssisted(false);
                this.setOptionsState(1);
            }
        },
        cancleMoveImg(e) {
            const pos = e.target.dataset.position || ''
            if (pos == '') {
                this.setAssisted(false);
            }
        },
        /**通过移动中心点,移动图片*/
        moveImg(x, y) {
            const startPoint = this.data.startMovePoint;
            if (startPoint == null) return;
            let disX = (startPoint.x - x);
            let disY = (startPoint.y - y);
    
            const centerAndOrgin = this.getNewCenterAndOrgin(disX, disY);
            // 修改参数值
            this.setData({
                duration: false,
                startMovePoint:new Point(x,y),
                "currentOpt.scale.center": new Point(centerAndOrgin.center.x, centerAndOrgin.center.y),
                "currentOpt.centerPoint": centerAndOrgin.orgin,

            })
        },
        /**缩放图片 */
        scaleImage(points) {
            // 通过两指间的距离计算缩放倍数
            const startPoint = this.data.startMovePoint;
            const [x, y] = this.changePoint(points[0].pageX, points[0].pageY);
            const [x1, y1] = this.changePoint(points[1].pageX, points[1].pageY);
            if (startPoint == null) return;
            if (startPoint.length == 2) {
                const dis1 = Math.sqrt(Math.pow(startPoint[0].x - startPoint[1].x, 2) + Math.pow(startPoint[0].y - startPoint[1].y, 2));
                const dis2 = Math.sqrt(Math.pow(x - x1, 2) + Math.pow(y - y1, 2));
                // 用两指滑动的起始和结束的距离差与容器宽度比作为缩放比例
                const width = this.data.componentInfo.getWidth();

                const ratio = Math.min(Math.max(1, this.data.startRatio + (dis2 - dis1) * 5 / width), 4);
                this.setData({
                    "currentOpt.scale.ratio": ratio
                })
            }
        },
        //================================
        /**角度变化开始 */
        sliderStart() {
            this.setAssisted(true, 25);
        },
        /**角度变化结束 */
        sliderEnd() {
            this.setAssisted(false, 25);
            this.setOptionsState(1);
        },


        // 计算裁剪框与组件比例
        getCropboxWithComponetRatio() {
            const cropboxRect = this.data.currentOpt.cropbox.rect;
            const componentInfo = this.data.componentInfo;
            // 本次缩放比例
            const ratio = Math.min(componentInfo.getWidth() / cropboxRect.getWidth(), componentInfo.getHeight() / cropboxRect.getHeight());
            return ratio;

        },
        /**辅助函数
         * 根据旋转角度\翻转等计算移动距离
         */
        getNewCenterAndOrgin(disX, disY) {
            const currentOpt = this.data.currentOpt;
            const componentCenter = this.data.componentInfo.getCenter();

            // 计算缩放原点  考虑翻转情况
            let horizontal = 1;
            let vertical = 1;
            if (currentOpt.overturn.horizontal == 180) horizontal = -1;
            if (currentOpt.overturn.vertical == 180) vertical = -1;
            disX = disX * horizontal;
            disY = disY * vertical;
            // 处理角度
            const angle = currentOpt.angle / 180 * Math.PI;
            disX = disX * Math.cos(angle) + disY * Math.sin(angle);
            disY = disY * Math.cos(angle) - disX * Math.sin(angle);

            // 移动后实际中心点
            const newCenter = new Point(currentOpt.scale.center.x + disX, currentOpt.scale.center.y + disY);
            const orginX = (componentCenter.x - newCenter.x);
            const orginY = (componentCenter.y - newCenter.y);
            return {
                center: new Point(newCenter.x, newCenter.y),
                orgin: new Point(orginX, orginY)
            };
        },
        scaleChage(e) {
            this.setData({
                "scale.ratio": e.detail.scale
            })
        },
        // 初始化方法
        init() {
            let _this = this;
            // 首先加载图片宽度和高度
            wx.getImageInfo({
                src: _this.data.imgSource,
                success: (imgInfo) => {
                    // 获取组件的宽度和高度
                    this.getComponentSize().then(rect => {
                        // 计算缩放后图片的显示位置
                        const imgPosition = _this.getImagePosition(rect, imgInfo);
                        // 设置4个拖拽框位置
                        _this.setDragBoxPosition(imgPosition);
                        // 计算图片的按容器缩放后的初始比例
                        const ratio = Math.max(imgInfo.width / rect.width, imgInfo.height / rect.height);
                        //创建操作队列
                        let opt = new Option();
                        opt.cropbox.rect = new Size(imgPosition);
                        opt.scale.rect = new Size(imgPosition);
                        opt.scale.center = imgPosition.getCenter();
                        opt.scale.baseRatio = ratio;
                        const options = new OptionStatic(opt);
                        // 保存中间参数信息
                        this.setData({
                            sourceImgInfo: new Size(imgPosition),
                            componentInfo: new Size({ left: 0, top: 0, right: rect.width, bottom: rect.height }),
                            currentOpt: opt,
                            options: options,
                            // "currentOpt.cropbox.rect": new Size(imgPosition),
                            // "currentOpt.scale.rect": new Size(imgPosition),
                            // "currentOpt.scale.center": imgPosition.getCenter(),
                            // "currentOpt.scale.baseRatio": ratio,
                        });
                    });
                },
            });
            this.setOptionsState();
        },
        // 设置4个拖拽框位置
        setDragBoxPosition(imgPosition) {
            const dragBoxWidth = 20;
            const dragBoxBorder = 5;
            this.setData({
                leftTop: { x: imgPosition.left, y: imgPosition.top },
                rightTop: { x: imgPosition.right - dragBoxWidth, y: imgPosition.top },
                rightBottom: { x: imgPosition.right - dragBoxWidth, y: imgPosition.bottom - dragBoxWidth },
                leftBottom: { x: imgPosition.left, y: imgPosition.bottom - dragBoxWidth },
            })
        },
        // 根据image组件尺寸和图片尺寸计算缩放后图片的显示位置
        getImagePosition(rect, imgInfo) {
            // 计算image组件与图片的尺寸比例
            const ratio = Math.max(imgInfo.width / rect.width, imgInfo.height / rect.height);
            const left = (rect.width - imgInfo.width / ratio) / 2;
            const top = (rect.height - imgInfo.height / ratio) / 2;
            const right = left + imgInfo.width / ratio;
            const bottom = top + imgInfo.height / ratio;

            return new Size({ left: left, top: top, right: right, bottom: bottom, width: right - left, height: bottom - top });
        },
        // 获取组件宽度和高度
        getComponentSize() {
            return new Promise((resolve, reject) => {
                var obj = wx.createSelectorQuery().in(this);
                obj.select('.img').boundingClientRect();
                obj.exec(function (rect) {
                    resolve(rect[0]);
                });
            })
        },

        /**当裁剪框边框发生变化时,计算多边形裁剪框顶点*/
        generateCropbox(data = null) {
            const polygonType = this.data.currentOpt.cropbox.type;
            let left = this.data.currentOpt.cropbox.rect.left;
            let right = this.data.currentOpt.cropbox.rect.right;
            let top = this.data.currentOpt.cropbox.rect.top;
            let bottom = this.data.currentOpt.cropbox.rect.bottom;
            if (data != null) {
                left = data.left;
                right = data.right;
                top = data.top;
                bottom = data.bottom;
            }
            let width = right - left;
            let height = bottom - top;
            let edgeLength = Math.min(width, height);
            let centerX = left + width / 2;
            let centerY = top + height / 2;
            let points = [];
            // none: 0, // 不显示裁剪框
            // rect: 1, //矩形裁剪框
            // polygon: 2, //多边形裁剪框
            // spec: 3, //指定边长比例裁剪框,如1寸证件照
            // sourceScale: 4, //原图片比例
            // heart: 5 ///心形裁剪框
            const param = this.data.currentOpt.cropbox.param || 1;
            switch (polygonType) {
                case CropMode.polygon: //多边形裁剪框
                    points = this.initPolygonCropbox(centerX, centerY, edgeLength, param);
                    break;
                case CropMode.rect: //矩形裁剪框
                    points = this.initRectCropbox(left, top, right, bottom);
                    break;
                case CropMode.spec: //指定边长比例裁剪框,如1寸证件照
                    points = this.initSpecCropbox(centerX, centerY, right - left, bottom - top, param);
                    break;
                case CropMode.sourceScale: //原图片比例
                    points = this.initSourceScaleCropbox(centerX, centerY, right - left, bottom - top);
                    break;
                case CropMode.heart: //心形裁剪框
                    const radius = (Math.min(width, height) / 32 - 1);
                    points = this.initHeartCropbox(centerX, centerY, radius);
                    break;
                default: // 不显示裁剪框
                    points = [new Point(left, top), new Point(right, top), new Point(right, bottom), new Point(left, bottom),];
            }
            // TODO：通过points计算css使用的裁剪框定点
            var cssClipPath = this.generateClipPathForCss(points);

            return { points: points, clipPath: cssClipPath };
        },
        generateClipPathForCss(points) {
            if (points.length > 0) {
                let unit = 'px';
                let clipPath = [];
                // 将顶点转为数组形式
                points.forEach(point => {
                    clipPath.push(point.x + unit + " " + point.y + unit);
                })
                // 将第一个顶点添加到数组末尾,对裁剪框进行闭合
                clipPath.push(clipPath[0]);
                // 将顶点数组转换为字符串形式,并拼接图片边框
                var tmpPath = clipPath.join(',') + ',0 0, 0 100%, 100% 100%, 100% 0, 0 0';
            }
            return tmpPath;
        },
        // 矩形裁剪框
        initRectCropbox(left, top, right, bottom) {

            const halfWidth = (right + left) / 2;
            const halfHeight = (bottom + top) / 2;

            let points = [
                new Point(left, top), //左上角
                new Point(halfWidth, top), //上中间
                new Point(right, top), //右上角
                new Point(right, halfHeight), //右中间
                new Point(right, bottom), // 右下角
                new Point(halfWidth, bottom), // 底中间
                new Point(left, bottom), // 左下角
                new Point(left, halfHeight), // 左中间
            ];

            return points;
        },
        /** 指定边长比例的规格裁剪框
         * @param {Number} ratio 长宽比例
         */
        initSpecCropbox(centerX, centerY, rectWidth, rectHeight, ratio = 1) {
            // 确定长度和高度
            let height = rectHeight;
            let width = height / ratio;
            if (width > rectWidth) {
                width = rectWidth;
                height = width * ratio;
            }

            let left = centerX - width / 2;
            let right = centerX + width / 2;
            let top = centerY - height / 2;
            let bottom = centerY + height / 2;
            let points = [
                new Point(left, top), //左上角
                new Point(right, top), //右上角
                new Point(right, bottom), // 右下角
                new Point(left, bottom), // 左下角
            ];

            return points;
        },
        /** 原图比例裁剪框
         */
        initSourceScaleCropbox(centerX, centerY, rectWidth, rectHeight) {

            const ratio = this.data.sourceImgInfo.getHeight() / this.data.sourceImgInfo.getWidth();
            // 保存裁剪框
            const points = this.initSpecCropbox(centerX, centerY, rectWidth, rectHeight, ratio);
            return points;
        },
        /**多边形裁剪框
         * @param {Number} sides 多边形边数
         */
        initPolygonCropbox(centerX, centerY, edgeLength, sides = 3) {
            // 最少为三个顶点
            if (sides < 3) {
                sides = 3;
            }
            const ratio = 1; // cropper.disWithOffRatio;

            // 显示区域最小的边长作为多边形的直径
            const length = edgeLength;
            let points = [],
                angle = 0,
                radius = 0,
                centerAngel = (2 * Math.PI) / sides;

            // 根据顶点数的奇数偶数的不同计算角度和偏移量,
            // 使偶边数的多边形上下两个边平行显示,奇数边的多边形在中间位置显示
            if (sides % 2 === 0) {
                //偶数顶点多边形，4、6、8边形
                angle += centerAngel / 2; //让底边水平
                radius = length / 2;
            } else {
                //奇数顶点多边形，3,5,7 边形
                let maxAngle = Math.round(Math.PI / 2 / centerAngel) * centerAngel;
                radius = length / 2 / Math.sin(maxAngle);
                centerY += radius - length / 2;
            }

            // 计算多边形顶点
            for (let i = 0; i < sides; i++) {
                let point = new Point(
                    ((centerX + radius * Math.sin(angle)) / ratio),
                    ((centerY - radius * Math.cos(angle)) / ratio),
                );
                points.push(point);
                angle += centerAngel;
            }
            return points;

        },
        /**
         * 心形裁剪框
         * @param {*} cx 
         * @param {*} cy 
         * @param {*} radius 
         */
        initHeartCropbox(cx = 0, cy = 0, radius = 1) {
            let vertices = [];

            // 固定点数
            const percision = 100;
            for (let i = 0; i < percision; i++) {
                var t = Math.PI * 2 * (i / percision - 0.5); //设置心上面两点之间的角度，具体分成多少份，好像需要去试。
                var vector = new Point(
                    (radius * 16 * (Math.sin(t) ** 3) + cx),
                    (-radius * (13 * Math.cos(t) - 5 * Math.cos(2 * t) - 2 * Math.cos(3 * t) - Math.cos(4 * t)) + cy)
                )
                vertices.push(vector);
            }

            return vertices;
        },
        /**父组件可调用方法 */
        /**翻转 
         * flag:0 水平翻转
         *      非0 垂直翻转
         */
        overturn(flag) {
            if (flag == 0) {
                // 水平翻转
                let deg = this.data.currentOpt.overturn.horizontal;
                if (deg == 0) {
                    deg = 180;
                } else {
                    deg = 0;
                }
                this.setData({
                    duration: false,
                    "currentOpt.overturn.horizontal": deg
                })
            } else {
                // 垂直翻转
                // 水平翻转
                let deg = this.data.currentOpt.overturn.vertical;
                if (deg == 0) {
                    deg = 180;
                } else {
                    deg = 0;
                }
                this.setData({
                    duration: false,
                    "currentOpt.overturn.vertical": deg
                })
            }
        },
        /**旋转图片 */
        rotate() {
            const angle = this.data.currentOpt.angle;
            this.setData({
                duration: false,
                "currentOpt.angle": (angle + 90) > 360 ? (angle + 90) - 360 : (angle + 90)
            })
        },
        /**重做 
         * flag =-1:pre,flag=1:next
         */
        redo(flag) {
            // const opt = this.data.currentOpt;
            if (flag == -1) {
                const opt = this.data.options.current();
                this.data.options.add(this.data.currentOpt);
                this.data.options.pre();
                this.setData({
                    "currentOpt": opt,
                })
                this.setOptionsState(2);

            }
            if (flag == 1) {
                this.data.options.next();
                const opt = this.data.options.current();
                this.setData({
                    "currentOpt": opt,
                })
                this.setOptionsState(1);
            }
        },
        /**设置操作序列状态 
         * flag:0 初始状态,1可以后退,2,可以前进,
         */
        setOptionsState(flag = 0) {
            let pre = false;
            let next = false;
            switch (flag) {
                case 1:
                    pre = true;
                    next = false;
                    // 重置操作序列
                    this.data.options.reset();
                    break;
                case 2:
                    pre = false;
                    next = true;
                    break;
                default:
                    break;
            }
            this.setData({
                "optionsState.pre": pre,
                "optionsState.next": next
            });
            this.triggerEvent("redo", {
                pre: pre,
                next: next
            });
        },
        /**设置裁剪框 */
        initCropbox(cropType, param) {
            this.setData({
                "currentOpt.cropbox.type": cropType,
                "currentOpt.cropbox.param": param || 1,
            });
            // 设置裁剪框位置
            let cropPoints = this.generateCropbox();
            this.setData({
                cssClipPath: cropPoints.clipPath
            });
        },
        //===============================
        // 裁剪图片相关==============================
        /** 旋转*/
        rotateImage(context, opt) {
            // 移动坐标原点到画布中心
            const center = opt.scale.center;
            const baseRatio = opt.scale.baseRatio;
            context.translate(center.x * baseRatio, center.y * baseRatio);
            // 旋转 参数角度以360为单位
            let a = (opt.angle / 180) * Math.PI;
            context.rotate(a);
            return {
                x: center.x * baseRatio,
                y: center.y * baseRatio
            }
        },
        /** 翻转 */
        overturnImage(context, opt, orgin) {
            if (opt.overturn.horizontal == 180) {
                // 水平翻转
                context.scale(-1, 1); //左右镜像翻转
            };
            if (opt.overturn.vertical == 180) {
                // 垂直翻转
                context.scale(1, -1); //垂直镜像翻转
            }

        },
        /**旋转组件事件方法 */
        getCurrAngle(e) {
            let angle = e.detail.currDegree;

            this.setData({
                duration: false,
                "currentOpt.angle": angle
            })

        },
        // 计算能够容纳原始尺寸图片的画布大小，以及图片在其中的位置
        getSourceImgBoardInfo() {
            const componentInfo = this.data.componentInfo;
            const imgInfo = this.data.sourceImgInfo;
            const baseRatio = this.data.currentOpt.scale.baseRatio;

            let canvasBoard = new Size({
                left: componentInfo.left * baseRatio,
                top: componentInfo.top * baseRatio,
                right: componentInfo.right * baseRatio,
                bottom: componentInfo.bottom * baseRatio,
            });
            let sourceImg = new Size({
                left: imgInfo.left * baseRatio,
                top: imgInfo.top * baseRatio,
                right: imgInfo.right * baseRatio,
                bottom: imgInfo.bottom * baseRatio,
            });
            return { canvasBoard: canvasBoard, imageInCavas: sourceImg };
        },
        /** 获取多边形最小矩形范围*/
        getPolygonMinRectSize(polygon) {
            // 重新设置多边形范围
            const xArr = Array.from(polygon, (x) => x.x);
            const yArr = Array.from(polygon, (x) => x.y);

            let left = Math.min(...xArr);
            let right = Math.max(...xArr);
            let top = Math.min(...yArr);
            let bottom = Math.max(...yArr);

            return new Size({
                left: left,
                top: top,
                right: right,
                bottom: bottom,
            });
        },
        /**
         * 创建离屏canvas
         * @param {*} width 
         * @param {*} height 
         */
        createOffCanvas(width, height) {
            const canvas = wx.createOffscreenCanvas({
                type: '2d',
                width: width,
                height: height
            });
            const context = canvas.getContext('2d');
            return {
                canvas: canvas,
                context: context
            };
        },
        /**
         * 父组件可调用方法
         * 按裁剪框裁剪图片并返回新图片
         */

        clipImage() {
            return new Promise((resolve, reject) => {

                let _this = this;
                // 获取当前操作参数
                const opt = this.data.currentOpt;
                const canvasInfo = this.getSourceImgBoardInfo().canvasBoard;

                // 创建离屏canvas并获得context
                const {
                    canvas,
                    context
                } = _this.createOffCanvas(canvasInfo.getWidth()*2, canvasInfo.getHeight()*2);

                const image = canvas.createImage();
                // 以下方式的写法是因为onload函数在真机环境只执行一次,
                var isLoaded = image.complete && image.naturalHeight !== 0;
                if (isLoaded) {
                    _this.clipOption(canvas, context, opt, image).then(path => {
                        resolve(path);
                    }, err => {
                        reject(err);
                    });
                }
                image.onload = () => {
                    _this.clipOption(canvas, context, opt, image).then(path => {
                        resolve(path);
                    }, err => {
                        reject(err);
                    });
                }
                // 为image赋值图片路径
                image.src = this.data.imgSource;
            })
        },

        /**裁剪图片动作,为了解决真机不执行onload问题 */
        clipOption(canvas, context, opt, image) {
            return new Promise((resolve, reject) => {
                // 绘制裁剪框
                const rect = this.drawCropBox(context, opt);
                // 绘制图片,包括旋转\翻转\等操作
                this.drawImage(context, image, opt);
                // 将裁剪框包括的图片裁剪
                wx.canvasToTempFilePath({
                    x: rect.left,
                    y: rect.top,
                    width: rect.getWidth(),
                    height: rect.getHeight(),
                    destWidth: rect.getWidth(),
                    destHeight: rect.getHeight(),
                    canvas: canvas,
                    success(res) {
                        // console.log(res.tempFilePath);
                        resolve(res.tempFilePath);
                    },
                    fail(err) {
                        reject(err);
                    }
                })
            })


        },
        /**按裁剪框裁剪图片*/
        drawCropBox(context, opt) {
            // 当前操作参数
            const scale = opt.scale;
            const rect = opt.cropbox.rect;
            // 计算在图片实际尺寸下裁剪框的尺寸和各个顶点位置
            // let edgeLength = Math.min(rect.right - rect.left, rect.bottom - rect.top) / scale.ratio ;
            const width = (rect.right - rect.left) / scale.ratio;
            const height = (rect.bottom - rect.top) / scale.ratio;
            const left = (scale.center.x - width / 2) * scale.baseRatio;
            const top = (scale.center.y - height / 2) * scale.baseRatio;
            const right = (scale.center.x + width / 2) * scale.baseRatio;
            const bottom = (scale.center.y + height / 2) * scale.baseRatio;
            let points = this.generateCropbox({ left, top, right, bottom }).points;////////////////////
            // 绘制裁剪框边框路径
            context.beginPath();
            context.moveTo(points[0].x, points[0].y);
            for (let i = 1; i < points.length; i++) {
                context.lineTo(points[i].x, points[i].y);
            }
            context.closePath();
            context.clip();
            // context.stroke();
            return this.getPolygonMinRectSize(points);

        },
        /** 绘制图片 */
        drawImage(context, image, opt) {
            const imgInfo = this.getSourceImgBoardInfo().imageInCavas;
            context.save();
            const orgin = this.rotateImage(context, opt);
            this.overturnImage(context, opt, orgin);
            context.globalCompositeOperation = "destination-over";
            context.drawImage(image, 0, 0, imgInfo.getWidth(), imgInfo.getHeight(), -1 * orgin.x + imgInfo.left, -1 * orgin.y + imgInfo.top, imgInfo.getWidth(), imgInfo.getHeight());
            context.restore();
        },
    },
    lifetimes: {
        ready() {
            this.init();
        }
    }
})